package com.qf.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.Properties;

@Component
@PropertySource("classpath:db.properties")
public class MyBatisUtils {

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("transactionManager")
    public DataSourceTransactionManager getDSTM(DataSource ds){
        //使用的数据源必须是和SqlSessionFactoryBean一致
        DataSourceTransactionManager dstm = new DataSourceTransactionManager();
        dstm.setDataSource(ds);
        return dstm;
    }

    /**
     * 配置数据源信息
     * @return
     */
    @Bean
    public DataSource getDs(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);

        return ds;
    }

    /**
     * 配置映射文件所在位置
     * ① 负责扫描所有的映射配置文件，可以是xml配置，规则和之前mybatis一致；
     *      也可以是注解配置，规则和之前mybatis一致
     *
     * ② 会基于dao层的接口，生成代理对象，并且将代理对象存入到spring的容器中
     *
     * ***注意：MapperScannerConfigurer是一个Bean工厂的后置处理器，是一个非常特殊的对象
     * 所以要求这个对象在MyBatisUtils之前被注入到Spring容器中，所以这个方法需要被static修饰
     *
     * Spring容器的创建过程：
     *    先将所有需要被管理的对象对应的类信息进行存储---Map<id,BeanDefinition>
     *    将这些BeanDefinition中属于BeanFactoryPostProcessor（bean工厂的后置处理器）类型的对象实例化，并且调用这些对象的方法
     *    将BeanDefinition中属于BeanPostProcessor（Bean后置处理器）类型的对象实例化，并存储到spring容器中（这里还没有执行这些对象的方法）
     *    将BeanDefinition中所有其他类型的对象【实例化】，并在在实例化后，会进行【属性赋值（DI）】，调用【初始化方法】，最后如果被从容器中移除，会执行【销毁方法】
     *              在Bean的声明周期过程中， 会在一些地方执行对应类型的Bean后置处理器方法，包括AOP过程，都是通过BeanPostProcessor完成
     *              在这个阶段，即使创建出来的是BeanFactoryPostProcessor类型的对象，也不会调用这些对象的方法的。
     */
    @Bean
    public static MapperScannerConfigurer msc(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        //告诉mybatis扫描哪个包下的映射文件
        msc.setBasePackage("com.qf.dao");

        return msc;
    }


    /**
     * 创建SqlSessionFactory对象
     * 负责创建SqlSessionFactory对象，并且通过连接池获取Connection对象，基于Connection对象构建SqlSession
     *
     * *** 方法参数上的@Autowired可以省略
     */
    @Bean
    public SqlSessionFactoryBean getSSFB(DataSource ds){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        //保证当前项目下所有数据源一致
        ssfb.setDataSource(ds);

        //实体包别名问题
        ssfb.setTypeAliasesPackage("com.qf.entity");

        //分页插件
        PageInterceptor pageInterceptor = new PageInterceptor();
        Properties pro = new Properties();
        pro.setProperty("helperDialect","mysql");
        pro.setProperty("reasonable","true");
        pageInterceptor.setProperties(pro);
        Interceptor[] plugins = new Interceptor[]{pageInterceptor};
        ssfb.setPlugins(plugins);

        return ssfb;
    }
}
